package com.listeningframework.boot.autoconfigure.orm.mybatis.support;

import com.listeningframework.boot.domains.Pagination;
import com.listeningframework.boot.domains.Paging;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.text.MessageFormat;
import java.util.List;

/**
 * 
 * Base Repository
 * @author lixining
 * @version $Id: BaseRepository.java, v 0.1 2015年4月9日 下午3:48:59 lixining Exp $
 */
public class ListeningMybatisRepository<T, ID extends Serializable> extends SqlSessionDaoSupport {
    /**
     * Entity Class Name
     */
    protected String entityClassName;
    /**
     * Entity Class
     */
    private Class<?> entityClass;
    /**
     * Insert Operator
     */
    private String   insert;
    /**
     * Select Operator
     */
    private String   select;
    /**
     * Select By PK
     */
    private String   selectByPK;
    /**
     * Count
     */
    private String   count;
    /**
     * Update By Condition
     */
    private String   update;
    /**
     * Update All
     */
    private String   updateAll;
    /**
     * Delete Records
     */
    private String   delete;

    /**
     * @see org.springframework.dao.support.DaoSupport#initDao()
     */
    public void initDao() throws Exception {
        getEntityClassName();
        handleBasicQuery();
    }

    /**
     * init basic query
     */
    private void handleBasicQuery() {
        this.insert = MessageFormat.format("{0}.insert", entityClassName);
        this.select = MessageFormat.format("{0}.select", entityClassName);
        this.selectByPK = MessageFormat.format("{0}.selectByPK", entityClassName);
        this.count = MessageFormat.format("{0}.count", entityClassName);
        this.delete = MessageFormat.format("{0}.delete", entityClassName);
        this.update = MessageFormat.format("{0}.update", entityClassName);
        this.updateAll = MessageFormat.format("{0}.updateAll", entityClassName);
    }

    /**
     * get entity class name
     */
    private void getEntityClassName() {
        Type type = super.getClass().getGenericSuperclass();
        if (type != null && type instanceof ParameterizedType) {
            ParameterizedType parameterType = (ParameterizedType) type;
            this.entityClass = (Class<?>) parameterType.getActualTypeArguments()[0];
        }
        if (this.entityClass != null) {
            this.entityClassName = entityClass.getSimpleName();
        }
        Assert.notNull(entityClassName, "entity class name cant be null");
    }

    /**
     * 
     * @see org.mybatis.spring.support.SqlSessionDaoSupport#setSqlSessionFactory(org.apache.ibatis.session.SqlSessionFactory)
     */
    @Autowired
    public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
        super.setSqlSessionFactory(sqlSessionFactory);
    }

    /**
     * 插入数据
     *
     * @param domain 插入对象
     * @return Integer
     */
    public Integer insert(T domain) {
        return getSqlSession().insert(this.insert, domain);
    }

    /**
     * 删除对象
     *
     * @param id 删除主键
     * @return Integer
     */
    public Integer delete(ID id) {
        return getSqlSession().delete(this.delete, id);
    }

    /**
     * 根据主键查询
     *
     * @param id 主键ID
     * @return T
     */
    public T select(ID id) {
        return getSqlSession().selectOne(this.selectByPK, id);
    }

    /**
     * 非空更新
     *
     * @param domain 保存对象
     * @return Integer
     */
    public Integer update(T domain) {
        return getSqlSession().update(this.update, domain);
    }

    /**
     * 全部更新
     *
     * @param domain 保存对象
     * @return Integer
     */
    public Integer updateAll(T domain) {
        return getSqlSession().update(this.updateAll, domain);
    }

    /**
     * 动态过滤
     *
     * @param domain 过滤条件
     * @return List
     */
    public List<T> filter(T domain) {
        return getSqlSession().selectList(this.select, domain);
    }

    /**
     * 分页
     *
     * @param domain 查询条件
     * @param page  分页信息
     * @return Pagination
     */
    public Pagination<T> page(T domain, Paging page) {
        Integer offSet = (page.getPage() - 1) * page.getLimit();
        Integer maxRow = page.getLimit();
        List<T> dataList = getSqlSession().selectList(this.select, domain, new RowBounds(offSet, maxRow));

        Pagination<T> pageResult = new Pagination<T>();
        pageResult.setTotalCount(count(domain));
        pageResult.setDataList(dataList);
        return pageResult;
    }

    /**
     * 分页查询
     *
     * @param domain 参数对象
     * @param pageNo 页码
     * @param pageSize 页面大小
     * @return Pagination
     */
    public Pagination<T> page(T domain, Integer pageNo, Integer pageSize) {
        Integer offSet = (pageNo - 1) * pageSize;
        Integer maxRow = pageSize;
        List<T> dataList = getSqlSession().selectList(this.select, domain, new RowBounds(offSet, maxRow));

        Pagination<T> pageResult = new Pagination<T>();
        pageResult.setTotalCount(count(domain));
        pageResult.setDataList(dataList);
        return pageResult;
    }

    /**
     * 计数
     *
     * @param domain 统计条件
     * @return Integer
     */
    private Integer count(T domain) {
        return getSqlSession().selectOne(this.count, domain);
    }

    /**
     * 格式化SQL ID
     *
     * @param pattern SQL标识
     * @return String
     */
    protected String format(String pattern) {
        return MessageFormat.format("{0}." + pattern, entityClassName);
    }
}
